# pymq - The python based Message Queue
#
# Copyright(C) 2010, Dhruv Matani(dhuvbird@gmail.com)
#
# pymq is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# pymq is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with pymq. If not, see
# <http://www.gnu.org/licenses/>.
#


def getCachingDataStore(baseDataStore, maxCacheLength):
    class CachingDataStore(baseDataStore):
        def __init__(self, dbName, *args, **kwargs):
            baseDataStore.__init__(self, dbName, *args, **kwargs)
            self.messageCache = { }
            self.maxCacheLength = maxCacheLength
            self.appendToQueue = False

        def __call__(self):
            return super(CachingDataStore, self).__call__()

        def getQueueCache(self, queueName):
            if queueName not in self.messageCache:
                self.messageCache[queueName] = [ ]
            return self.messageCache[queueName]

        def enqueueMessage(self, queueName, message):
            """Enqueue a message in the queue 'queueName'
            """
            enqueueResult = super(CachingDataStore, self).enqueueMessage(queueName, message)
            if not enqueueResult:
                return False
            lid = self.lastInsertedMessageID()

            cache = self.getQueueCache(queueName)
            if self.appendToQueue and len(cache) < self.maxCacheLength:
                print "Enqueuing message id %s" % lid
                cache.append({ "message": message, "messageID": lid })
            else:
                self.appendToQueue = False
            return enqueueResult

        def peekMessage(self, queueName):
            """Fetch the first(oldest) un-acknowledged message in the queue 'queueName'
            """
            cache = self.getQueueCache(queueName)
            if cache:
                return [cache[0]]
            else:
                # Try to refill the cache
                messages = self.peekMessages(queueName, self.maxCacheLength)
                if messages and len(messages) > 0:
                    if len(messages) == self.maxCacheLength:
                        self.appendToQueue = False
                    else:
                        self.appendToQueue = True
                    cache.extend(messages)
                    return [cache[0]]
                return []

        def ackMessage(self, queueName, messageID):
            """Acknowledge the message with ID 'messageID'.
            A message may be acknowledged multiple times.
            It should succeed all the times.
            """
            # The client should only ACK the head of the queue
            cache = self.getQueueCache(queueName)
            print "MESSAGE ID: %d" % messageID
            if cache:
                print "first message id: %d" % cache[0]['messageID']

            if cache and cache[0]['messageID'] == messageID:
                print "Shortening cache by 1. Cache is now: " + str(len(cache))
                cache.pop(0)
            return super(CachingDataStore, self).ackMessage(queueName, messageID)

        def getLastUnAckedMessageID(self, queueName):
            """Get the messageID of the last(oldest) un-acknowledged message from the queue 'queueName'
            """
            cache = self.getQueueCache(queueName)
            if cache:
                return [cache[0]['messageID']]
            return super(CachingDataStore, self).getLastUnAckedMessageID(queueName)

        def unprovisionQueue(self, queueName):
            """Delete all the messages from the queue 'queueName'
            """
            cache = self.getQueueCache(queueName)
            if cache is not None:
                del self.messageCache[queueName]
            super(CachingDataStore, self).unprovisionQueue(queueName)

    return CachingDataStore